package com.cjet.nissan.tcp

import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.os.Message
import android.util.Log
import com.cjet.nissan.tcp.INetty.OnChannelHandler
import com.cjet.nissan.tcp.INetty.OnConnectListener
import com.cjet.nissan.tcp.INetty.OnSendMessageListener
import com.cjet.nissan.tcp.ImpNetty
import com.cjet.nissan.tcp.codec.Encoder
import com.cjet.nissan.tcp.codec.ImprovedDecoder
import io.netty.bootstrap.Bootstrap
import io.netty.channel.ChannelFuture
import io.netty.channel.ChannelInitializer
import io.netty.channel.ChannelOption
import io.netty.channel.EventLoopGroup
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioSocketChannel
import io.netty.handler.codec.string.StringEncoder
import io.netty.handler.logging.LogLevel
import io.netty.handler.logging.LoggingHandler
import io.netty.util.concurrent.Future
import java.net.ConnectException
import java.net.InetSocketAddress
import java.net.SocketAddress
import kotlin.concurrent.Volatile

class ImpNetty : INetty {
    /**
     * 是否debug，true则会显示打印日志
     */
    private val isDebug: Boolean

    @Volatile
    private var isReconnect = false

    private var mHandlerThread: HandlerThread? = null

    private var mHandler: Handler? = null

    private var mMainHandler: Handler? = null

    private var mSocketAddress: SocketAddress? = null

    private var mChannelFuture: ChannelFuture? = null

    private var mBootstrap: Bootstrap? = null

    private var mGroup: EventLoopGroup? = null

    private var mChannelInitializer: ChannelInitializer<SocketChannel>? = null

    private var mOnConnectListener: OnConnectListener? = null

    private var mOnSendMessageListener: OnSendMessageListener? = null

    /**
     * 构造
     *
     * @param onChannelHandler [OnChannelHandler]
     * @param isDebug          是否启用debug模式；方便查看日志
     */
    /**
     * 构造
     *
     * @param onChannelHandler [OnChannelHandler]
     */
    @JvmOverloads
    constructor(onChannelHandler: OnChannelHandler?, isDebug: Boolean = false) {
        this.isDebug = isDebug
        this.mChannelInitializer = object : ChannelInitializer<SocketChannel>() {
            override fun initChannel(ch: SocketChannel) {
                //建立管道
                val channelPipeline = ch.pipeline()
                //添加相关编码器，解码器，处理器等
                channelPipeline //                        .addLast(new StringEncoder())
                    //                        .addLast(new StringDecoder())
                    .addLast(StringEncoder())//FA 2C
                    .addLast(Encoder(byteArrayOf(0xfa.toByte(),0x2c)))
                    .addLast(ImprovedDecoder(mMainHandler,onChannelHandler))
            }
        }
        initHandlerThread()
        mHandler!!.sendEmptyMessage(NETTY_INIT)
    }

    /**
     * 构造
     *
     * @param channelInitializer [ChannelInitializer]
     * @param isDebug            是否启用debug模式；方便查看日志
     */
    constructor(channelInitializer: ChannelInitializer<SocketChannel>?, isDebug: Boolean) {
        this.isDebug = isDebug
        this.mChannelInitializer = channelInitializer
        initHandlerThread()
        mHandler!!.sendEmptyMessage(NETTY_INIT)
    }

    /**
     * 构造
     *
     * @param bootstrap [Bootstrap]
     * @param isDebug   是否启用debug模式；方便查看日志
     */
    /**
     * 构造
     *
     * @param bootstrap [Bootstrap]
     */
    @JvmOverloads
    constructor(bootstrap: Bootstrap, isDebug: Boolean = false) {
        this.mBootstrap = bootstrap
        this.isDebug = isDebug
        this.mGroup = bootstrap.config().group()
        initHandlerThread()
    }

    /**
     * 初始化
     */
    @Synchronized
    private fun initHandlerThread() {
        mMainHandler = Handler(Looper.getMainLooper())
        mHandlerThread = HandlerThread(ImpNetty::class.java.simpleName)
        mHandlerThread!!.start()

        mHandler = object : Handler(mHandlerThread!!.looper) {
            override fun handleMessage(msg: Message) {
                super.handleMessage(msg)
                when (msg.what) {
                    NETTY_INIT -> handleNettyInit()
                    NETTY_CONNECT -> handleConnect()
                    NETTY_SEND_MESSAGE -> handleSendMessage(msg.obj)
                    else -> {}
                }
            }
        }
    }

    /**
     * 处理Netty初始化
     */
    private fun handleNettyInit() {
        mBootstrap = Bootstrap()
        mBootstrap!!.channel(NioSocketChannel::class.java)
        mGroup = NioEventLoopGroup()
        mBootstrap!!.group(mGroup)
            .option(ChannelOption.TCP_NODELAY, true)
            .option(ChannelOption.SO_REUSEADDR, true)
            .handler(LoggingHandler(if (isDebug) LogLevel.DEBUG else LogLevel.INFO))
        mBootstrap!!.handler(mChannelInitializer)
    }

    /**
     * 处理连接
     */
    private fun handleConnect() {
        try {
            if (isDebug) {
                Log.d(TAG, "connect: $mSocketAddress")
            }
            isReconnect = false
            mChannelFuture = mBootstrap!!.connect(mSocketAddress)
                .addListener { future: Future<in Void?> ->
                    val isSuccess = future.isSuccess
                    if (isSuccess) {
                        if (isDebug) {
                            Log.d(TAG, "Netty connection is successful.")
                        }
                        if (mOnConnectListener != null) {
                            mMainHandler!!.post { mOnConnectListener!!.onSuccess() }
                        }
                    } else {
                        throw Exception(future.cause())
                    }
                }.sync()
        } catch (e: Throwable) {
            if (isDebug) {
                Log.w(TAG, "Netty connection is failed.")
            }
            if (mOnConnectListener != null) {
                mMainHandler!!.post { mOnConnectListener!!.onFailure(e) }
            }
        }
    }

    /**
     * 处理发送消息
     *
     * @param msg 消息
     */
    private fun handleSendMessage(msg: Any?): Boolean {
        try {
            if (!isConnected()) {
                throw ConnectException("Netty channel is not connected.")
            }
            mChannelFuture!!.channel().writeAndFlush(msg).addListener { future: Future<in Void?> ->
                val isSuccess = future.isSuccess
                if (isSuccess) {
                    if (isDebug) {
                        Log.d(TAG, "Send message: $msg")
                    }
                    if (mOnSendMessageListener != null) {
                        mMainHandler!!.post { mOnSendMessageListener!!.onSendMessage(msg) }
                    }
                } else {
                    throw Exception(future.cause())
                }
            }.sync()
            return true
        } catch (e: Throwable) {
            if (isDebug) {
                Log.w(TAG, "Message sending failed.", e)
            }
            if (mOnSendMessageListener != null) {
                mMainHandler!!.post { mOnSendMessageListener!!.onException(e) }
            }
        }

        return false
    }

    override fun connect(host: String?, port: Int) {
        connect(InetSocketAddress.createUnresolved(host, port))
    }

    override fun connect(socketAddress: SocketAddress?) {
        if (mSocketAddress == socketAddress && isConnected() && !isReconnect) {
            return
        }
        this.mSocketAddress = socketAddress
        mHandler!!.sendEmptyMessage(NETTY_CONNECT)
    }

    override fun connectBlocking(host: String?, port: Int): Boolean {
        return connectBlocking(InetSocketAddress.createUnresolved(host, port))
    }

    override fun connectBlocking(socketAddress: SocketAddress?): Boolean {
        if (mSocketAddress == socketAddress && isConnected() && !isReconnect) {
            return true
        }
        this.mSocketAddress = socketAddress
        handleConnect()
        return isConnected()
    }

    override fun reconnect(delayMillis: Long) {
        disconnect()
        isReconnect = true
        mHandler!!.sendEmptyMessageDelayed(NETTY_CONNECT, delayMillis)
    }

    override fun reconnectBlocking(): Boolean {
        disconnectBlocking()
        isReconnect = true
        return connectBlocking(mSocketAddress)
    }

    override fun sendMessage(msg: Any?) {
        mHandler!!.obtainMessage(NETTY_SEND_MESSAGE, msg).sendToTarget()
    }

    override fun sendMessageBlocking(msg: Any?): Boolean {
        return handleSendMessage(msg)
    }

    override fun setOnConnectListener(listener: OnConnectListener?) {
        this.mOnConnectListener = listener
    }

    override fun setOnSendMessageListener(listener: OnSendMessageListener?) {
        this.mOnSendMessageListener = listener
    }

    override fun close() {
        if (isOpen()) {
            disconnect()
            mChannelFuture!!.channel().close()
            if (isDebug) {
                Log.d(TAG, "Netty channel has been closed.")
            }
        }
    }

    override fun closeBlocking(): Boolean {
        if (isOpen()) {
            disconnectBlocking()
            mChannelFuture!!.channel().close().syncUninterruptibly()
            if (isDebug) {
                Log.d(TAG, "Netty channel has been closed.")
            }
        }
        return !isOpen() && !isConnected()
    }

    override fun disconnect() {
        if (isConnected()) {
            mChannelFuture!!.channel().disconnect()
            if (isDebug) {
                Log.d(TAG, "Netty channel has been disconnected.")
            }
        }
    }

    override fun disconnectBlocking(): Boolean {
        if (isConnected()) {
            mChannelFuture!!.channel().disconnect().syncUninterruptibly()
            if (isDebug) {
                Log.d(TAG, "Netty channel has been disconnected.")
            }
        }
        return !isConnected()
    }

    override fun isConnected(): Boolean {
        val isConnected = mChannelFuture != null && mChannelFuture!!.channel().isActive
        if (isDebug && !isConnected) {
            Log.w(TAG, "Netty channel is not connected.")
        }
        return isConnected
    }

    override fun isOpen(): Boolean {
        val isOpen = mChannelFuture != null && mChannelFuture!!.channel().isOpen
        if (isDebug && !isOpen) {
            Log.w(TAG, "Netty channel is not opened.")
        }
        return isOpen
    }

    override fun getChannelFuture(): ChannelFuture? {
        return mChannelFuture
    }

    companion object {
        val TAG: String = ImpNetty::class.java.simpleName

        private const val NETTY_INIT = 0x01
        private const val NETTY_CONNECT = 0x02
        private const val NETTY_SEND_MESSAGE = 0x03

        const val DEFAULT_CHARSET: String = "UTF-8"

        /**
         * 默认消息结束分隔符；EOT
         */
        val DEFAULT_DELIMITER: ByteArray = byteArrayOf(0x04)

        /**
         * 默认传输内容最大长度 128K
         */
        const val DEFAULT_MAX_FRAME_LENGTH: Int = 131072
    }
}
